package com.ag.oa.netty;

import com.ag.oa.core.entity.dto.WebSocketMsgDTO;
import com.ag.oa.redis.RedisUtils;
import com.ag.oa.util.BaseUtils;
import com.ag.oa.util.DateUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 处理在线聊天功能
 */
public class OnlineWebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    private String nettyOrigin;
    private RedisUtils redisUtils;

    public OnlineWebSocketHandler(String nettyOrigin, RedisUtils redisUtils) {
        this.nettyOrigin = nettyOrigin;
        this.redisUtils = redisUtils;
    }

    private final Logger logger = LoggerFactory.getLogger(OnlineWebSocketHandler.class);

    /**
     * 存储已经登录用户的channel，在线的用户通道
     */
    public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * 存储用户id 和 用户的channelId绑定
     */
    public static ConcurrentHashMap<Integer, ChannelId> userMap = new ConcurrentHashMap<>();

    /**
     * 指定 房间号 和 聊天成员的channel信息客户端收到服务器数据
     * 1、 随机房间id
     * 2、 随机房间id 与 多个用户id 绑定
     */
    public static ConcurrentHashMap<String, ChannelGroup> groupMap = new ConcurrentHashMap<>();

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        logger.info("与客户端建立连接，通道开启！");
        // 添加到channelGroup通道组
        channelGroup.add(ctx.channel());
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        logger.info("与客户端断开连接，通道关闭！");
        // channelGroup 通道组中移除
        channelGroup.remove(ctx.channel());
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 首次连接是FullHttpRequest，把用户id和对应的channel对象存储起来
        if (null != msg && msg instanceof FullHttpRequest) {
            FullHttpRequest request = (FullHttpRequest) msg;

            String requestOrigin = request.headers().get("Origin");
            logger.info("requestOrigin = " + requestOrigin);
            if (requestOrigin == null || !requestOrigin.startsWith(nettyOrigin)) {
                // 请求头来源校验，不符合的请求来源，拒绝webSocket连接
                channelGroup.remove(ctx.channel());
                ctx.close();
                return;
            }

            String uri = request.uri();
            Map<String, String> paramsMap = BaseUtils.getUrlParamsMap(uri);
            if (StringUtils.isBlank(paramsMap.get("userId")) || paramsMap.size() == 0) {
                // 参数不符合要求，拒绝webSocket连接
                channelGroup.remove(ctx.channel());
                ctx.close();
                return;
            }

            Integer userId = Integer.parseInt(paramsMap.get("userId"));
            // 登录后把用户id和channelId关联上
            userMap.put(userId, ctx.channel().id());
            logger.info("登录的用户id是：{}", userId);

            // 如果url包含参数，需要处理
            if (uri.contains("?")) {
                String newUri = uri.substring(0, uri.indexOf("?"));
                request.setUri(newUri);
            }

            System.out.println("世界频道的人数： "+channelGroup.size());
        } else if (msg instanceof TextWebSocketFrame) {
            // 正常的TEXT消息类型
            TextWebSocketFrame frame = (TextWebSocketFrame) msg;
            logger.info("客户端收到服务器数据：{}", frame.text());
            WebSocketMsgDTO onlineMessage = JSON.parseObject(frame.text(), WebSocketMsgDTO.class);

            /*
            后端增加需要的字段信息
            1. chat_id 每条，消息的唯一id
            2. chat_date 消息日期（使用服务器时间）
	        3. send_time 发送时间（使用服务器时间）
            */
            String chatId = UUID.randomUUID().toString();
            onlineMessage.setChatId(chatId);
            Date now = new Date();
            String chatDate = DateUtils.format(now,"yyyy-MM-dd");
            String sendTime = DateUtils.format(now,"yyyy-MM-dd HH:mm:ss");
            String showSendTime = DateUtils.format(now,"MM月dd日 HH:mm");
            onlineMessage.setChatDate(chatDate);
            onlineMessage.setSendTime(sendTime);
            onlineMessage.setShowSendTime(showSendTime);

            String messageType = onlineMessage.getMessageType();
            if ("3".equals(messageType)) {
                // 世界频道聊天的时候，固定key
                String key = "world_channel";
                onlineMessage.setRoomId(key);

                // 消息存入redis，48小时有效，并且最多只保留200条数据
                if(redisUtils.lGetListSize(key) >= 200){
                    redisUtils.lLeftPop(key);
                }
                redisUtils.lSet(key, onlineMessage, 48 * 60 * 60);

                // 世界频道 (只要有在线用户，则发送消息)
                if (channelGroup.size() > 0) {
                    channelGroup.writeAndFlush(new TextWebSocketFrame(JSONObject.toJSONString(onlineMessage)));
                }
            } else {
                String key = onlineMessage.getRoomId();
                // 单聊 或者 指定房间的群聊 逻辑
                // 后续补充...
            }
        }
        super.channelRead(ctx, msg);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame)
            throws Exception {
    }

}
